//    Copyright (C) Kherty.  All rights reserved.
#region

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics.Contracts;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using OpenLS.Spreadsheet.Drawing;
using OpenLS.Spreadsheet.UI.Internals;
using OpenLS.Spreadsheet.UI.Internals.Resources;
using OpenLS.UI;

#endregion

namespace OpenLS.Spreadsheet.UI
{
using documentContainer = DocumentContainer;

    /// <summary>
    /// Defines a set of attached properties and event related to spreadsheets.
    /// </summary>
    public class SpreadsheetElement : FrameworkElement
    {
        /// <summary>
        /// Commit the text entry to the worksheet
        /// </summary>
        /// <param name="target">The UIElement hosting the worksheet</param>
        public static void CommitText (FrameworkElement target)
        {
            RoutedEventArgs newEventArgs = new RoutedEventArgs(SpreadsheetElement.CommittingTextEvent);
            var desc = Enumerable.OfType<CellTextBox>(WindowServices.GetDescendants(target));
            foreach (FrameworkElement item in desc)
                item.RaiseEvent(newEventArgs);
//            focusedElement.RaiseEvent(newEventArgs);
        }

        // Create a custom routed event by first registering a RoutedEventID
        // This event uses the tunnel routing strategy
        public static readonly RoutedEvent CommittingTextEvent = EventManager.RegisterRoutedEvent(
            "CommittingText", RoutingStrategy.Tunnel, typeof(RoutedEventHandler), typeof(FrameworkElement));

        public event RangeChangedEventHandler CommittingText
        {
            add { AddHandler(CommittingTextEvent, value); }
            remove { RemoveHandler(CommittingTextEvent, value); }
        }


        // This event is raised when the user attempts to change a protected worksheet
        // This event uses the tunnel routing strategy
        public static readonly RoutedEvent SheetProtectionViolationEvent = EventManager.RegisterRoutedEvent(
            "SheetProtectionViolation", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FrameworkElement));

        public event RoutedEventHandler SheetProtectionViolation
        {
            add { AddHandler(SheetProtectionViolationEvent, value); }
            remove { RemoveHandler(SheetProtectionViolationEvent, value); }
        }

        public static void OnProtectionViolation(UIElement source)
        {
            if (source != null)
            {
                RoutedEventArgs routedEventArgs = new RoutedEventArgs(SheetProtectionViolationEvent, source);
                source.RaiseEvent(routedEventArgs);
                if (routedEventArgs.Handled)
                    return;
            }
            MessageBox.Show(UIStringTable.Protected);
        }

        public static bool GetWasLoaded(DependencyObject obj)
        {
            return (bool)obj.GetValue(WasLoadedProperty);
        }

        public static void SetWasLoaded(DependencyObject obj, bool value)
        {
            obj.SetValue(WasLoadedProperty, value);
        }

        // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty WasLoadedProperty =
            DependencyProperty.RegisterAttached("WasLoaded", typeof(bool), typeof(SpreadsheetElement), new UIPropertyMetadata(false));


        public static SelectionAndActiveCell GetWorkingSelectionAndActiveCell(DependencyObject obj)
        {
            return (SelectionAndActiveCell)obj.GetValue(WorkingSelectionAndActiveCellProperty);
        }
        public static Range GetWorkingSelection(object sender)
        {
            var selectionAndActiveCell = GetWorkingSelectionAndActiveCell((DependencyObject) sender);
            if (selectionAndActiveCell != null)
                return selectionAndActiveCell.Range;
            Worksheet ws = WorksheetHelper.GetWorksheet(sender);
            
            return ws.Selection;
        }
        public static Cell GetWorkingActiveCell(object sender)
        {
            Contract.Ensures(Contract.Result<Cell>() != null);
            var selectionAndActiveCell = GetWorkingSelectionAndActiveCell((DependencyObject)sender);
            if (selectionAndActiveCell != null)
                return selectionAndActiveCell.Cell;
            Worksheet ws = WorksheetHelper.GetWorksheet(sender);

            return ws.ActiveCell;
        }
        public static CellAddress? GetWorksheetActiveCellAddress(object sender)
        {
            var c = GetWorkingActiveCell(sender);
            if (c != null)
                return c.Address;
            return null;
        }
        public static void SetWorkingSelectionAndActiveCell(DependencyObject obj, SelectionAndActiveCell value)
        {
            obj.SetValue(WorkingSelectionAndActiveCellProperty, value);
        }

        // Using a DependencyProperty as the backing store for WorkingSelectionAndActiveCell.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty WorkingSelectionAndActiveCellProperty =
            DependencyProperty.RegisterAttached("WorkingSelectionAndActiveCell", typeof(SelectionAndActiveCell), typeof(SpreadsheetElement), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits));

        
        public static readonly RoutedEvent PreviewSelectionChangingEvent = EventManager.RegisterRoutedEvent("PreviewSelectionChanging",
                                                                                                 RoutingStrategy.Tunnel,
                                                                                                 typeof(
                                                                                                     RangeChangedEventHandler
                                                                                                     ),
                                                                                                 typeof(
                                                                                                     SpreadsheetElement
                                                                                                     ));

       


        public static readonly RoutedEvent SelectionChangedEvent = EventManager.RegisterRoutedEvent("SelectionChanged",
                                                                                                  RoutingStrategy.Bubble,
                                                                                                  typeof(
                                                                                                      RangeChangedEventHandler
                                                                                                      ),
                                                                                                  typeof(
                                                                                                      SpreadsheetElement
                                                                                                      ));

        [Category("Behavior")]
        public event RangeChangedEventHandler SelectionChanged
        {
            add { AddHandler(SelectionChangedEvent, value); }
            remove { RemoveHandler(SelectionChangedEvent, value); }
        }
        [Category("Behavior")]
        public event RangeChangedEventHandler PreviewSelectionChanging
        {
            add { AddHandler(PreviewSelectionChangingEvent, value); }
            remove { RemoveHandler(PreviewSelectionChangingEvent, value); }
        }


        public static DataTemplateSelector GetCellEditorTemplateSelector(DependencyObject obj)
        {
            return (DataTemplateSelector)obj.GetValue(CellEditorTemplateSelectorProperty);
        }

        public static void SetCellEditorTemplateSelector(DependencyObject obj, DataTemplateSelector value)
        {
            obj.SetValue(CellEditorTemplateSelectorProperty, value);
        }

        // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CellEditorTemplateSelectorProperty =
            DependencyProperty.RegisterAttached("CellEditorTemplateSelector", typeof(DataTemplateSelector), typeof(SpreadsheetElement), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits));


        public static WorksheetViewModel GetWorksheetViewModel(DependencyObject obj)
        {
            return (WorksheetViewModel)obj.GetValue(WorksheetViewModelProperty);
        }

        public static void SetWorksheetViewModel(DependencyObject obj, WorksheetViewModel value)
        {
            obj.SetValue(WorksheetViewModelProperty, value);
        }

        // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty WorksheetViewModelProperty =
            DependencyProperty.RegisterAttached("WorksheetViewModel", typeof(WorksheetViewModel), typeof(SpreadsheetElement), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits));



        public static Anchor GetSelectedAnchor(DependencyObject obj)
        {
            return (Anchor)obj.GetValue(SelectedAnchorProperty);
        }

        public static void SetSelectedAnchor(DependencyObject obj, Anchor value)
        {
            obj.SetValue(SelectedAnchorProperty, value);
        }

        // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SelectedAnchorProperty =
            DependencyProperty.RegisterAttached("SelectedAnchor", typeof(Anchor), typeof(SpreadsheetElement), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits));




        public static ColorMode GetColorMode(DependencyObject obj)
        {
            return (ColorMode)obj.GetValue(ColorModeProperty);
        }

        public static void SetColorMode(DependencyObject obj, ColorMode value)
        {
            obj.SetValue(ColorModeProperty, value);
        }

        // Using a DependencyProperty as the backing store for ColorMode.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ColorModeProperty =
            DependencyProperty.RegisterAttached("ColorMode", typeof(ColorMode), typeof(SpreadsheetElement), new FrameworkPropertyMetadata(ColorMode.Color, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.Inherits));


        public static readonly RoutedEvent FormulaChangedEvent = EventManager.RegisterRoutedEvent("FormulaChanged",
                                                                                                  RoutingStrategy.Direct,
                                                                                                  typeof(
                                                                                                      RoutedEventHandler
                                                                                                      ),
                                                                                                  typeof(
                                                                                                      SpreadsheetElement
                                                                                                      ));

        [Category("Behavior")]
        public event RoutedEventHandler FormulaChanged
        {
            add { AddHandler(FormulaChangedEvent, value); }
            remove { RemoveHandler(FormulaChangedEvent, value); }
        }

        public static Formula GetFormula(DependencyObject obj)
        {
            return (Formula)obj.GetValue(FormulaProperty);
        }

        public static void SetFormula(DependencyObject obj, Formula value)
        {
            obj.SetValue(FormulaProperty, value);
        }

        // Using a DependencyProperty as the backing store for Formula.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty FormulaProperty =
            DependencyProperty.RegisterAttached("Formula", typeof(Formula), typeof(SpreadsheetElement),
                                                new UIPropertyMetadata(null, OnFormulaChanged));


        private static void OnFormulaChanged(DependencyObject owner, DependencyPropertyChangedEventArgs args)
        {
            UIElement e = owner as UIElement;
            if (e == null)
                return;
            e.RaiseEvent(new RoutedEventArgs(FormulaChangedEvent, e));
        }

        public static readonly RoutedEvent WorksheetChangedEvent = EventManager.RegisterRoutedEvent("WorksheetChanged",
                                                                                                    RoutingStrategy.
                                                                                                        Direct,
                                                                                                    typeof(
                                                                                                        RoutedEventHandler
                                                                                                        ),
                                                                                                    typeof(
                                                                                                        SpreadsheetElement
                                                                                                        ));

        [Category("Behavior")]
        public event RoutedEventHandler WorksheetChanged
        {
            add { AddHandler(WorksheetChangedEvent, value); }
            remove { RemoveHandler(WorksheetChangedEvent, value); }
        }


        public static BackgroundOperation GetOperation(DependencyObject obj)
        {
            return (BackgroundOperation)obj.GetValue(OperationProperty);
        }

        public static void SetOperation(DependencyObject obj, BackgroundOperation value)
        {
            obj.SetValue(OperationProperty, value);
        }

        // Using a DependencyProperty as the backing store for Operation.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty OperationProperty =
            DependencyProperty.RegisterAttached("Operation", typeof(BackgroundOperation), typeof(SpreadsheetElement),
                                                new FrameworkPropertyMetadata(null,
                                                                              FrameworkPropertyMetadataOptions.Inherits));


        public static ObservableCollection<documentContainer> GetDocuments(DependencyObject obj)
        {
            return (ObservableCollection<documentContainer>)obj.GetValue(DocumentsProperty);
        }

        public static void SetDocuments(DependencyObject obj, ObservableCollection<documentContainer> value)
        {
            obj.SetValue(DocumentsProperty, value);
        }

        // Using a DependencyProperty as the backing store for Documents.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DocumentsProperty =
            DependencyProperty.RegisterAttached("Documents", typeof(ObservableCollection<documentContainer>),
                                                typeof(SpreadsheetElement),
                                                new FrameworkPropertyMetadata(null,
                                                                              FrameworkPropertyMetadataOptions.Inherits));


        public static BlockNavigationMode GetBlockNavigation(DependencyObject obj)
        {
            return (BlockNavigationMode)obj.GetValue(BlockNavigationProperty);
        }

        public static void SetBlockNavigation(DependencyObject obj, BlockNavigationMode value)
        {
            obj.SetValue(BlockNavigationProperty, value);
        }

        // Using a DependencyProperty as the backing store for BlockNavigation.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty BlockNavigationProperty =
            DependencyProperty.RegisterAttached("BlockNavigation", typeof(BlockNavigationMode),
                                                typeof(SpreadsheetElement),
                                                new UIPropertyMetadata(BlockNavigationMode.ByCell));


        public static Range GetDropTarget(DependencyObject obj)
        {
            return (Range)obj.GetValue(DropTargetProperty);
        }

        public static void SetDropTarget(DependencyObject obj, Range value)
        {
            obj.SetValue(DropTargetProperty, value);
        }

        // Using a DependencyProperty as the backing store for DropTarget.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DropTargetProperty =
            DependencyProperty.RegisterAttached("DropTarget", typeof(Range), typeof(SpreadsheetElement),
                                                new FrameworkPropertyMetadata(null,
                                                                              FrameworkPropertyMetadataOptions.Inherits));


        public static Range GetHighlight(DependencyObject obj)
        {
            return (Range)obj.GetValue(HighlightProperty);
        }

        public static void SetHighlight(DependencyObject obj, Range value)
        {
            obj.SetValue(HighlightProperty, value);
        }

        // Using a DependencyProperty as the backing store for Highlight.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty HighlightProperty =
            DependencyProperty.RegisterAttached("Highlight", typeof(Range), typeof(SpreadsheetElement),
                                                new FrameworkPropertyMetadata(null,
                                                                              FrameworkPropertyMetadataOptions.Inherits));


        public static Workbook GetWorkbook(DependencyObject obj)
        {
            return (Workbook)obj.GetValue(WorkbookProperty);
        }

        public static void SetWorkbook(DependencyObject obj, Workbook value)
        {
            obj.SetValue(WorkbookProperty, value);
        }

        // Using a DependencyProperty as the backing store for Workbook.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty WorkbookProperty =
            DependencyProperty.RegisterAttached("Workbook", typeof(Workbook), typeof(SpreadsheetElement),
                                                new FrameworkPropertyMetadata(null,
                                                                              FrameworkPropertyMetadataOptions.Inherits,
                                                                              OnWorkbookChanged));

        private static void OnWorkbookChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue == e.OldValue)
                return;
            if (e.NewValue == null)
                return;
            if (e.OldValue == null)
                return;
            DependencyObject root = EditionElement.GetEditorRoot(d);
            if (root == d)
            {
                EditionElement.SetEditedCell(d, null);
            }
        }

        public static Worksheet GetWorksheet(object obj)
        {
            DependencyObject o = obj as DependencyObject;
            if (o == null)
                return null;
            return (Worksheet)o.GetValue(WorksheetProperty);
        }

        public static Range GetSelection(object obj)
        {
            Worksheet ws = GetWorksheet(obj);
            if (ws == null)
                return null;
            return ws.Selection;
        }

        public static void SetWorksheet(DependencyObject obj, Worksheet value)
        {
            obj.SetValue(WorksheetProperty, value);
        }

        // Using a DependencyProperty as the backing store for Worksheet.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty WorksheetProperty =
            DependencyProperty.RegisterAttached("Worksheet", typeof(Worksheet), typeof(SpreadsheetElement),
                                                new FrameworkPropertyMetadata(null,
                                                                              FrameworkPropertyMetadataOptions.Inherits,
                                                                              OnWorksheetChanged));


        private static void OnWorksheetChanged(DependencyObject owner, DependencyPropertyChangedEventArgs args)
        {
            UIElement e = owner as UIElement;
            if (e == null)
                return;
            e.RaiseEvent(new RoutedEventArgs(WorksheetChangedEvent, e));
        }


        public static IWorksheetFragment GetWorksheetFragment(DependencyObject obj)
        {
            return (IWorksheetFragment)obj.GetValue(WorksheetFragmentProperty);
        }

        public static void SetWorksheetFragment(DependencyObject obj, IWorksheetFragment value)
        {
            obj.SetValue(WorksheetFragmentProperty, value);
        }


        // Using a DependencyProperty as the backing store for WorksheetFragment.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty WorksheetFragmentProperty =
            DependencyProperty.RegisterAttached("WorksheetFragment", typeof(IWorksheetFragment), typeof(SpreadsheetElement), new FrameworkPropertyMetadata(null,
                                                                              FrameworkPropertyMetadataOptions.Inherits));


        public static WorksheetPane GetWorksheetPane(DependencyObject obj)
        {
            return (WorksheetPane)obj.GetValue(WorksheetPaneProperty);
        }

        public static void SetWorksheetPane(DependencyObject obj, WorksheetPane value)
        {
            obj.SetValue(WorksheetPaneProperty, value);
        }

        // Using a DependencyProperty as the backing store for WorksheetPane.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty WorksheetPaneProperty =
            DependencyProperty.RegisterAttached("WorksheetPane", typeof(WorksheetPane), typeof(SpreadsheetElement),
                                                new FrameworkPropertyMetadata(null,
                                                                              FrameworkPropertyMetadataOptions.Inherits));


        public static void SetHighlightFor(object sender, Range value)
        {
            DependencyObject target = EditionElement.GetEditorRoot((DependencyObject)sender);
            SetHighlight(target, value);
        }

        public static void ForgetHightlightForAndClipboardMonitor(object sender)
        {
            DependencyObject target = EditionElement.GetEditorRoot((DependencyObject)sender);
            SetHighlight(target, null);
            ClipboardMonitor.ClearMonitor(target);
        }
        public static void SetDropTargetFor(object sender, Range value)
        {
            SetDropTarget(EditionElement.GetEditorRoot((DependencyObject)sender), value);
        }


        public static AutoFill GetAutoFill(DependencyObject obj)
        {
            return (AutoFill)obj.GetValue(AutoFillProperty);
        }

        public static void SetAutoFill(DependencyObject obj, AutoFill value)
        {
            obj.SetValue(AutoFillProperty, value);
        }

        // Using a DependencyProperty as the backing store for AutoFill.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AutoFillProperty =
            DependencyProperty.RegisterAttached("AutoFill", typeof(AutoFill), typeof(SpreadsheetElement),
                                                new FrameworkPropertyMetadata(null,
                                                                              FrameworkPropertyMetadataOptions.Inherits));


        public static AutoFill GetAutoFillFor(object sender)
        {
            DependencyObject e = EditionElement.GetEditorRoot((DependencyObject)sender);
            return GetAutoFill(e);
        }

        public static void SetAutoFillFor(object sender, AutoFill value)
        {
            DependencyObject e = EditionElement.GetEditorRoot((DependencyObject)sender);
            SetAutoFill(e, value);
        }

        /// <summary>
        /// Set the current range of 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="range"></param>
        public static void SetCurrentRangeForRoot(object sender, Range range, Cell activeCell)
        {
            Contract.Requires(range != null || activeCell != null);
            Contract.Requires(range != null);
            Contract.Requires(sender != null);

            bool isSelectionValid = (range == null || activeCell == null || range.Contains(activeCell));
            if (!isSelectionValid) return;

            // Contract.Assert(range == null || activeCell == null || range.Contains(activeCell));
            Worksheet worksheet = WorksheetHelper.GetWorksheet(sender);
            var root = EditionElement.GetEditorRoot((DependencyObject)sender);
           // SetWorkingSelectionAndActiveCell(root, new SelectionAndActiveCell(range, activeCell));
            RangeChangedEventArgs eventArgs = new RangeChangedEventArgs(PreviewSelectionChangingEvent, range, activeCell);
            FrameworkElement sender2 = (FrameworkElement)sender;
            sender2.RaiseEvent(eventArgs);
            if (eventArgs.Handled)
            {
                SetWorkingSelectionAndActiveCell(root, new SelectionAndActiveCell(range, activeCell));
                return;
            }
            if (range != null)
            {
                worksheet.Selection = range;
                if (activeCell != null)
                    worksheet.ActiveCell = activeCell;
            }
            else
            {
                worksheet.Selection = activeCell.ToRange();
                worksheet.ActiveCell = activeCell;
            }
            
            eventArgs = new RangeChangedEventArgs(SelectionChangedEvent, range, activeCell);
            sender2.RaiseEvent(eventArgs);

        }

        public static void AddSelectionChanged(UIElement uiElement, RangeChangedEventHandler handler)
        {
            uiElement.AddHandler(SelectionChangedEvent, handler);
        }
        public static void RemoveSelectionChanged(UIElement uiElement, RangeChangedEventHandler handler)
        {
            uiElement.RemoveHandler(SelectionChangedEvent, handler);
        }
        public static void AddPreviewSelectionChanging(UIElement uiElement, RangeChangedEventHandler handler)
        {
            uiElement.AddHandler(PreviewSelectionChangingEvent, handler);
        }
        public static void RemovePreviewSelectionChanging(UIElement uiElement, RangeChangedEventHandler handler)
        {
            uiElement.RemoveHandler(PreviewSelectionChangingEvent, handler);
        }
    }
}